
#include "macros.hpp"
#include "SplineR2.hpp"
#include "Parametrization.hpp"
#include "CLParametrization.hpp"
#include "CentripetalParametrization.hpp"
#include <iostream>
#include <fstream>
#include <vector>
#include <cstdlib>
using namespace std;

enum ParamType { UNIFORM, CHORD_LENGTH, CENTRIPETAL };

struct Input
{
	vector<Spline::Point> control_points;
	uint samples;
	Spline::Point click_point, move_point;
};

void usage( char* p )
{
	cerr << "Uso: " << p << " [input] [output]" << endl;
}

Input read_input( char* file )
{
	ifstream f( file );
	Input input;
	uint n;

	// lee la primera línea (puntos, samples)
	f >> n >> input.samples;

	// lee los puntos de control
	forn(i,n-1)
	{
		double x, y;
		f >> x >> y;
		input.control_points.push_back( Spline::Point(x,y) );
	}

	// obtiene el punto "clickeado"
	f >> input.click_point.x >> input.click_point.y;

	// obtiene el punto a donde se mueve
	f >> input.move_point.x >> input.move_point.y;

	f.close();

	return input;
}

ParamType getParametrization( int argc, char** argv )
{
	ParamType parametrization = UNIFORM;
	if( argc >= 5 && argv[3][0] == '-' && argv[3][1] == 'p' )
		parametrization = (ParamType)atoi(argv[4]);
	return parametrization;
}

int main( int argc, char** argv )
{
	if( argc < 3 )
	{
		usage(argv[0]);
		return 1;
	}

	Input input = read_input(argv[1]);
	
	Parametrization* parametrization;
	switch(	getParametrization(argc,argv) )
	{
		case CHORD_LENGTH: 
			parametrization = new CLParametrization( input.control_points );
			break;
		case CENTRIPETAL:
			parametrization = new CentripetalParametrization( input.control_points );
			break;
		default:
			parametrization = new UniformParametrization( input.control_points );
			break;
	}

	SplineR2 spline( input.control_points, *parametrization );

	delete parametrization;

	// Obtiene la muestra de puntos del spline
	vector<Spline::Point> sample = spline.sample( input.samples );

	// Guarda el muestreo de la spline y la derivada 
	ofstream output(argv[2]);
	double step = 1.0f/(input.samples-1);
	uint s=0;
	foreach(point, sample)
	{
		double t = (s++)*step;

		Spline::Point derivative  = spline.derivative( t );
		Spline::Point derivative2 = spline.derivative2( t );

		double delta_x = (input.click_point.x-point->x);
		double delta_y = (input.click_point.y-point->y);

		// f(t) = (d²)' [simplificadas]
		double ft  = -delta_x*derivative.x - delta_y*derivative.y;
		// f'(t) = (d²)''
		double dft = -delta_x*derivative2.x + derivative.x*derivative.x
                     -delta_y*derivative2.y + derivative.y*derivative.y;

		output << t << '\t' 
		       << delta_x*delta_x+delta_y*delta_y << '\t'
		       << 2*ft << '\t'
			   << 2*dft << endl;

/*		
		output << t << '\t' 
		       << point->x << '\t'
		       << point->y << '\t'
			   << derivative.x << '\t'
			   << derivative.y << '\t'
			   << derivative2.x << '\t'
			   << derivative2.y << endl;
*/
	}
	output.close();
	
	return 0;
}

